package org.xjy.sequence.impl;


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xjy.sequence.SequenceDao;
import org.xjy.sequence.SequenceRange;
import org.xjy.sequence.exception.SequenceException;

public class DefaultSequenceDao implements SequenceDao {
    private static final Log log = LogFactory.getLog(DefaultSequenceDao.class);
    private static final int MIN_STEP = 1;
    private static final int MAX_STEP = 10000;
    private static final int DEFAULT_STEP = 100;
    private static final int DEFAULT_RETRY_TIMES = 150;
    private static final String DEFAULT_TABLE_NAME = "sequence";
    private static final String DEFAULT_NAME_COLUMN_NAME = "name";
    private static final String DEFAULT_VALUE_COLUMN_NAME = "value";
    private static final String DEFAULT_GMT_MODIFIED_COLUMN_NAME = "gmt_modified";
    private static final long DELTA = 100000000L;
    private DataSource dataSource;
    private int retryTimes = 150;
    private int step = 100;
    private String tableName = "sequence";
    private String nameColumnName = "name";
    private String valueColumnName = "value";
    private String gmtModifiedColumnName = "gmt_modified";
    private volatile String selectSql;
    private volatile String updateSql;

    public DefaultSequenceDao() {
    }

    public SequenceRange nextRange(String name) throws SequenceException {
        if (name == null) {
            throw new IllegalArgumentException("序列名称不能为空");
        } else {
            Connection conn = null;
            PreparedStatement stmt = null;
            ResultSet rs = null;

            for(int i = 0; i < this.retryTimes + 1; ++i) {
                long oldValue;
                long newValue;
                try {
                    conn = this.dataSource.getConnection();
                    stmt = conn.prepareStatement(this.getSelectSql());
                    stmt.setString(1, name);
                    rs = stmt.executeQuery();
                    rs.next();
                    oldValue = rs.getLong(1);
                    StringBuilder message;
                    if (oldValue < 0L) {
                        message = new StringBuilder();
                        message.append("Sequence value cannot be less than zero, value = ").append(oldValue);
                        message.append(", please check table ").append(this.getTableName());
                        throw new SequenceException(message.toString());
                    }

                    if (oldValue > 9223372036754775807L) {
                        message = new StringBuilder();
                        message.append("Sequence value overflow, value = ").append(oldValue);
                        message.append(", please check table ").append(this.getTableName());
                        throw new SequenceException(message.toString());
                    }

                    newValue = oldValue + (long)this.getStep();
                } catch (SQLException var23) {
                    throw new SequenceException(var23);
                } finally {
                    closeResultSet(rs);
                    rs = null;
                    closeStatement(stmt);
                    stmt = null;
                    closeConnection(conn);
                    conn = null;
                }

                SequenceRange var11;
                try {
                    conn = this.dataSource.getConnection();
                    stmt = conn.prepareStatement(this.getUpdateSql());
                    stmt.setLong(1, newValue);
                    stmt.setTimestamp(2, new Timestamp(System.currentTimeMillis()));
                    stmt.setString(3, name);
                    stmt.setLong(4, oldValue);
                    int affectedRows = stmt.executeUpdate();
                    if (affectedRows == 0) {
                        continue;
                    }

                    var11 = new SequenceRange(oldValue + 1L, newValue);
                } catch (SQLException var21) {
                    throw new SequenceException(var21);
                } finally {
                    closeStatement(stmt);
                    stmt = null;
                    closeConnection(conn);
                    conn = null;
                }

                return var11;
            }

            throw new SequenceException("Retried too many times, retryTimes = " + this.retryTimes);
        }
    }

    private String getSelectSql() {
        if (this.selectSql == null) {
            synchronized(this) {
                if (this.selectSql == null) {
                    StringBuilder buffer = new StringBuilder();
                    buffer.append("select ").append(this.getValueColumnName());
                    buffer.append(" from ").append(this.getTableName());
                    buffer.append(" where ").append(this.getNameColumnName()).append(" = ?");
                    this.selectSql = buffer.toString();
                }
            }
        }

        return this.selectSql;
    }

    private String getUpdateSql() {
        if (this.updateSql == null) {
            synchronized(this) {
                if (this.updateSql == null) {
                    StringBuilder buffer = new StringBuilder();
                    buffer.append("update ").append(this.getTableName());
                    buffer.append(" set ").append(this.getValueColumnName()).append(" = ?, ");
                    buffer.append(this.getGmtModifiedColumnName()).append(" = ? where ");
                    buffer.append(this.getNameColumnName()).append(" = ? and ");
                    buffer.append(this.getValueColumnName()).append(" = ?");
                    this.updateSql = buffer.toString();
                }
            }
        }

        return this.updateSql;
    }

    private static void closeResultSet(ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException var2) {
                log.debug("Could not close JDBC ResultSet", var2);
            } catch (Throwable var3) {
                log.debug("Unexpected exception on closing JDBC ResultSet", var3);
            }
        }

    }

    private static void closeStatement(Statement stmt) {
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException var2) {
                log.debug("Could not close JDBC Statement", var2);
            } catch (Throwable var3) {
                log.debug("Unexpected exception on closing JDBC Statement", var3);
            }
        }

    }

    private static void closeConnection(Connection conn) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException var2) {
                log.debug("Could not close JDBC Connection", var2);
            } catch (Throwable var3) {
                log.debug("Unexpected exception on closing JDBC Connection", var3);
            }
        }

    }

    public DataSource getDataSource() {
        return this.dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public int getRetryTimes() {
        return this.retryTimes;
    }

    public void setRetryTimes(int retryTimes) {
        if (retryTimes < 0) {
            throw new IllegalArgumentException("Property retryTimes cannot be less than zero, retryTimes = " + retryTimes);
        } else {
            this.retryTimes = retryTimes;
        }
    }

    public int getStep() {
        return this.step;
    }

    public void setStep(int step) {
        if (step >= 1 && step <= 10000) {
            this.step = step;
        } else {
            StringBuilder message = new StringBuilder();
            message.append("Property step out of range [").append(1);
            message.append(",").append(10000).append("], step = ").append(step);
            throw new IllegalArgumentException(message.toString());
        }
    }

    public String getTableName() {
        return this.tableName;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public String getNameColumnName() {
        return this.nameColumnName;
    }

    public void setNameColumnName(String nameColumnName) {
        this.nameColumnName = nameColumnName;
    }

    public String getValueColumnName() {
        return this.valueColumnName;
    }

    public void setValueColumnName(String valueColumnName) {
        this.valueColumnName = valueColumnName;
    }

    public String getGmtModifiedColumnName() {
        return this.gmtModifiedColumnName;
    }

    public void setGmtModifiedColumnName(String gmtModifiedColumnName) {
        this.gmtModifiedColumnName = gmtModifiedColumnName;
    }
}

